Skip to content

Comments

feat: calendar view atom v1#23840

Merged
Ryukemeister merged 23 commits intomainfrom
calendar-view-atom-v1
Sep 22, 2025
Merged

feat: calendar view atom v1#23840
Ryukemeister merged 23 commits intomainfrom
calendar-view-atom-v1

Conversation

@Ryukemeister
Copy link
Contributor

@Ryukemeister Ryukemeister commented Sep 15, 2025

What does this PR do?

  • This PR adds a new atom called calendar view which is basically a read only calendar that users can integrate in their apps

Image Demo:

Screenshot 2025-09-17 at 7 25 20 PM Screenshot 2025-09-17 at 7 25 32 PM

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • (N/A) I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • (N/A) I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

This can be tested in the web app, i've created a new page for this called /calendar-view

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds calendar-view functionality and refactors booking calendar internals. Header now accepts an optional isCalendarView prop, initializes week-start via a new useInitializeWeekStart hook when appropriate, adjusts month/week view logic, and changes the Today button behavior in calendar mode. Introduces useAvailableTimeSlots and replaces inline slot computations in multiple LargeCalendar components. Adds a new calendar-view LargeCalendar, a CalendarViewPlatformWrapper exported as CalendarView, an example page and navbar link, and exports formatUsername from BookerPlatformWrapper. Also updates a selected-calendars import style.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "feat: calendar view atom v1" is concise, single-sentence, and directly describes the primary change (adding a CalendarView atom v1), so it accurately communicates the main intent of the changeset to reviewers.
Description Check ✅ Passed The PR description describes the change (adds a read-only calendar view atom), includes motivation, demo screenshots, and testing instructions (new /calendar-view page), and is directly related to the changeset shown in the diff, so it meets the lenient description criteria.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch calendar-view-atom-v1

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c940118 and 41d54d1.

📒 Files selected for processing (1)
  • packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx
🧠 Learnings (1)
📚 Learning: 2025-08-29T22:57:31.407Z
Learnt from: bandhan-majumder
PR: calcom/cal.com#23454
File: packages/features/bookings/Booker/components/EventMeta.tsx:16-16
Timestamp: 2025-08-29T22:57:31.407Z
Learning: In Cal.com's Booker architecture, components become client-side through BookerStoreProvider.tsx which has "use client". Any component using useBookerStoreContext automatically runs on the client and should use client-appropriate utilities like markdownToSafeHTMLClient, regardless of whether they have explicit "use client" directives.

Applied to files:

  • packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx
🧬 Code graph analysis (1)
packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx (2)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
  • BookerStoreProvider (15-22)
packages/features/eventtypes/components/EventType.tsx (1)
  • EventType (71-116)
🔇 Additional comments (2)
packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx (2)

6-6: LGTM: Import correctly added for BookerStoreProvider.

The import follows the established pattern and aligns with the component integration changes.


421-437: Confirm BookerStoreProvider usage matches the established pattern.

The integration follows the recommended Zustand pattern using useRef to ensure the store is created only once, as seen in the BookerStoreProvider implementation from the relevant code snippets. This change allows EventType to access the Booker store context while maintaining all existing props and functionality.

Verify that this change aligns with the broader calendar-view integration by checking how other calendar-related wrappers use BookerStoreProvider:


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@keithwillcode keithwillcode added core area: core, team members only platform Anything related to our platform plan labels Sep 15, 2025
@vercel
Copy link

vercel bot commented Sep 15, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Sep 22, 2025 11:40am
cal-eu Ignored Ignored Sep 22, 2025 11:40am

@Ryukemeister Ryukemeister marked this pull request as ready for review September 17, 2025 13:56
@Ryukemeister Ryukemeister requested a review from a team September 17, 2025 13:56
@Ryukemeister Ryukemeister requested a review from a team as a code owner September 17, 2025 13:56
@dosubot dosubot bot added the ✨ feature New feature or request label Sep 17, 2025
Comment on lines +117 to +128
useEffect(() => {
const searchParams = props.routingFormSearchParams
? new URLSearchParams(props.routingFormSearchParams)
: new URLSearchParams(window.location.search);

const routedTeamMemberIds = getRoutedTeamMemberIdsFromSearchParams(searchParams);
const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true";

const _cacheParam = searchParams?.get("cal.cache");
const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined;
const isBookingDryRun =
searchParams?.get("cal.isBookingDryRun")?.toLowerCase() === "true" ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we can in a followup pr let's make a custom hook for the search params and share the hook between components

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is duplicated from booker platform wrapper

Comment on lines +129 to +136
searchParams?.get("cal.sandbox")?.toLowerCase() === "true";
setRoutingParams({
...(skipContactOwner ? { skipContactOwner } : {}),
...(routedTeamMemberIds ? { routedTeamMemberIds } : {}),
...(_shouldServeCache ? { _shouldServeCache } : {}),
...(isBookingDryRun ? { isBookingDryRun } : {}),
});
}, [props.routingFormSearchParams]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search param hook

supalarry
supalarry previously approved these changes Sep 22, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)

526-537: Localize or avoid hardcoded "Function not implemented." strings.

If these surface, they should be behind t(); otherwise replace with no‑ops to avoid throwing in production.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc26b4 and d65d2bb.

📒 Files selected for processing (1)
  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
🔇 Additional comments (1)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)

598-603: Allow null/undefined and trim inputs in formatUsername

Use:

export function formatUsername(username: string | string[] | null | undefined): string {
  if (!username) return "";
  if (typeof username === "string") return username.trim();
  return username.filter(Boolean).map((u) => u.trim()).join("+");
}

Before merging, confirm there are no other definitions/imports/usages of formatUsername by running locally:
rg -n --hidden -S 'export\s+(const|function)\s+formatUsername' --glob '!node_modules/' || true
rg -n --hidden -S 'import.*formatUsername' --glob '!node_modules/
' || true
rg -n --hidden -S '\bformatUsername\b' --glob '!node_modules/**' || true

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Remove file‑wide exhaustive‑deps disable; scope it to specific hooks.

A blanket disable can hide stale-closure bugs across this hook-heavy file. Prefer fixing deps or narrowly disabling per‑hook (you already do this in a few places).

Apply:

-/* eslint-disable react-hooks/exhaustive-deps */

Run to audit where we still rely on blanket vs scoped disables:


🏁 Script executed:

#!/bin/bash
# Hooks and nearby eslint disables for this file
rg -n -C1 -e 'useEffect\(|useMemo\(|useCallback\(' packages/platform/atoms/booker/BookerPlatformWrapper.tsx
rg -n 'eslint-disable-next-line react-hooks/exhaustive-deps' packages/platform/atoms/booker/BookerPlatformWrapper.tsx

Length of output: 2023


Remove file-wide eslint-disable react-hooks/exhaustive-deps. Scoped disables already cover the five specific hooks at lines 153, 158, 231, 468, and 490—drop the blanket disable at the top.

-/* eslint-disable react-hooks/exhaustive-deps */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/* eslint-disable react-hooks/exhaustive-deps */
🤖 Prompt for AI Agents
In packages/platform/atoms/booker/BookerPlatformWrapper.tsx around line 1,
remove the file-wide comment "/* eslint-disable react-hooks/exhaustive-deps */"
and leave the existing scoped eslint-disable comments at lines 153, 158, 231,
468, and 490 intact; after removing the blanket disable, run ESLint (or your IDE
linter) to confirm there are no new react-hooks/exhaustive-deps warnings and
address any remaining specific hook warnings by scoping disables only to the
individual lines where necessary.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx (5)

43-43: Consider using more precise type for view prop.

The default value "MONTH_VIEW" suggests this should be typed as keyof typeof BookerLayouts rather than allowing any string.

Consider adding proper typing in the types file for this prop to ensure type safety:

-    view = "MONTH_VIEW",
+    view = BookerLayouts.MONTH_VIEW,

107-108: Memoization has inconsistent dependencies.

The username dependency is included but the logic only uses the string length check which shouldn't change if username remains the same formatted value.

-  const isDynamic = useMemo(() => {
-    return getUsernameList(username ?? "").length > 1;
-  }, [username]);
+  const isDynamic = getUsernameList(username ?? "").length > 1;

If memoization is truly needed for performance, consider memoizing the getUsernameList call itself.


180-189: Complex enabled condition could be simplified.

The enabled condition combines multiple checks that could be extracted for readability and testability.

+  const isDataReady = props.isTeamEvent ? !isTeamPending : !isPending;
+  const hasRequiredIds = Boolean(teamId || username);
+  const hasSchedulingContext = Boolean(month) && Boolean(timezone) && Boolean(event?.data?.id);
+  
   enabled:
-    Boolean(teamId || username) &&
-    Boolean(month) &&
-    Boolean(timezone) &&
-    (props.isTeamEvent ? !isTeamPending : !isPending) &&
-    Boolean(event?.data?.id),
+    hasRequiredIds && hasSchedulingContext && isDataReady,

199-201: Magic numbers should be configurable or documented.

The component uses hard-coded values (extraDays=7, nextSlots=6) without explanation or configuration options.

Consider making these configurable or at least documenting why these specific values were chosen:

+// Calendar view shows a week (7 days) at a time
+const CALENDAR_VIEW_DAYS = 7;
+// Navigation moves by 6 slots in column view
+const COLUMN_VIEW_NAVIGATION_SLOTS = 6;

 <Header
   isCalendarView={true}
   isMyLink={true}
   eventSlug={eventSlug}
   enabledLayouts={bookerLayout.bookerLayouts.enabledLayouts}
-  extraDays={7}
+  extraDays={CALENDAR_VIEW_DAYS}
   isMobile={false}
-  nextSlots={6}
+  nextSlots={COLUMN_VIEW_NAVIGATION_SLOTS}
 />

195-196: Hard-coded calendar props OK for this wrapper; consider exposing them if reused

Header uses isMyLink (controls the troubleshooter button) and isCalendarView (drives calendar-specific behavior like week-start / "today" logic). CalendarViewPlatformWrapper sets isCalendarView={true} and isMyLink={true} in packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx:195–196 — correct for the platform-only calendar. Expose these as props only if you expect this wrapper to be reused in other contexts (embed/mobile).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d65d2bb and c940118.

📒 Files selected for processing (1)
  • packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx
🧠 Learnings (1)
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.

Applied to files:

  • packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx
🧬 Code graph analysis (1)
packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx (10)
packages/platform/atoms/booker/types.ts (2)
  • BookerPlatformWrapperAtomPropsForIndividual (91-95)
  • BookerPlatformWrapperAtomPropsForTeam (97-102)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)
  • formatUsername (598-603)
packages/features/bookings/Booker/BookerStoreProvider.tsx (3)
  • useBookerStoreContext (24-35)
  • useInitializeBookerStoreContext (37-113)
  • BookerStoreProvider (15-22)
packages/platform/atoms/hooks/event-types/public/useAtomGetPublicEvent.tsx (1)
  • useAtomGetPublicEvent (23-65)
packages/features/bookings/Booker/components/hooks/useBookerLayout.ts (1)
  • useBookerLayout (18-94)
packages/platform/atoms/hooks/bookings/useGetBookingForReschedule.ts (1)
  • useGetBookingForReschedule (17-54)
packages/platform/atoms/hooks/useAvailableSlots.ts (1)
  • useAvailableSlots (15-49)
packages/features/bookings/Booker/components/Section.tsx (1)
  • BookerSection (43-63)
packages/features/bookings/Booker/components/Header.tsx (1)
  • Header (21-175)
packages/features/calendar-view/LargeCalendar.tsx (1)
  • LargeCalendar (15-79)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Production builds / Build Atoms
  • GitHub Check: Production builds / Build Web App
  • GitHub Check: Production builds / Build API v2
  • GitHub Check: Type check / check-types
  • GitHub Check: Production builds / Build API v1
  • GitHub Check: Linters / lint
  • GitHub Check: Tests / Unit
🔇 Additional comments (5)
packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx (5)

32-34: Confirm the consolidated type approach is correct.

The component accepts either individual or team props via union type, which looks good. The previous issue with isTeamEvent?: false preventing passing true appears to have been addressed based on the commit message.


193-211: Good component structure with proper store context usage.

The component properly uses the AtomsWrapper, BookerSection components with appropriate areas, and passes the required data to child components. The sticky positioning and z-index management look correct.


215-223: Clean wrapper implementation with proper store provider.

The wrapper component correctly provides the BookerStore context that's required by the child component. This separation of concerns is well done.


117-136: Extract URL search params logic to custom hook.

This duplicates logic from BookerPlatformWrapper. Consider creating a shared hook as suggested in the past review comment.

Create a custom hook useBookingRoutingParams to share this logic between CalendarViewPlatformWrapper and BookerPlatformWrapper:

export function useBookingRoutingParams(routingFormSearchParams?: URLSearchParams | string) {
  return useMemo(() => {
    const searchParams = routingFormSearchParams
      ? new URLSearchParams(routingFormSearchParams)
      : new URLSearchParams(window.location.search);
    
    const routedTeamMemberIds = getRoutedTeamMemberIdsFromSearchParams(searchParams);
    const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true";
    const _cacheParam = searchParams?.get("cal.cache");
    const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined;
    const isBookingDryRun =
      searchParams?.get("cal.isBookingDryRun")?.toLowerCase() === "true" ||
      searchParams?.get("cal.sandbox")?.toLowerCase() === "true";
    
    return {
      ...(skipContactOwner ? { skipContactOwner } : {}),
      ...(routedTeamMemberIds ? { routedTeamMemberIds } : {}),
      ...(_shouldServeCache ? { _shouldServeCache } : {}),
      ...(isBookingDryRun ? { isBookingDryRun } : {}),
    };
  }, [routingFormSearchParams]);
}

155-156: Clarify semantics and precedence of rescheduleUid vs bookingUid.

Search returned no repo matches here — confirm the intended rule when both props are supplied. Either document that rescheduleUid overrides bookingUid (add an inline comment and tests), or make the props mutually exclusive / consolidate/rename them to remove ambiguity.
Location: packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx (props mapping).

Comment on lines +76 to +82
const monthCount =
((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
? 2
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Complex month count calculation needs documentation.

This complex conditional logic for determining monthCount is difficult to understand without context.

Add explanatory comment and consider extracting to a helper function:

+  // Determine if we need to prefetch two months of data when the view spans across month boundaries
+  const shouldPrefetchTwoMonths = () => {
+    const isTimeSelectionInNonWeekView = 
+      bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time";
+    const isColumnView = bookerLayout.layout === BookerLayouts.COLUMN_VIEW;
+    
+    if (!isTimeSelectionInNonWeekView && !isColumnView) return false;
+    
+    const viewEndDate = dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day");
+    return dayjs(date).add(1, "month").month() !== viewEndDate.month();
+  };
+  
-  const monthCount =
-    ((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
-      bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
-    dayjs(date).add(1, "month").month() !==
-      dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
-      ? 2
-      : undefined;
+  const monthCount = shouldPrefetchTwoMonths() ? 2 : undefined;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const monthCount =
((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
? 2
: undefined;
// Determine if we need to prefetch two months of data when the view spans across month boundaries
const shouldPrefetchTwoMonths = () => {
const isTimeSelectionInNonWeekView =
bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time";
const isColumnView = bookerLayout.layout === BookerLayouts.COLUMN_VIEW;
if (!isTimeSelectionInNonWeekView && !isColumnView) return false;
const viewEndDate = dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day");
return dayjs(date).add(1, "month").month() !== viewEndDate.month();
};
const monthCount = shouldPrefetchTwoMonths() ? 2 : undefined;
🤖 Prompt for AI Agents
In
packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx
around lines 76 to 82, the conditional computing monthCount is hard to read and
lacks context; extract this logic into a well-named helper (e.g.,
computeMonthCountForLayout) and add an explanatory comment above the helper
explaining the business intent (when to show 2 months: selecting_time in
non-week layouts or column view and the start and end dates fall in different
months). Move the dayjs arithmetic and boolean checks into the helper, return 2
or undefined, and replace the inline expression with a clear call to the helper
so the intent is documented and the component code is easier to read and test.

}, [props.routingFormSearchParams]);
const bookingData = useBookerStoreContext((state) => state.bookingData);
const setBookingData = useBookerStoreContext((state) => state.setBookingData);
const layout = BookerLayouts[view];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate view prop value.

The view prop is used to index into BookerLayouts without validation, which could cause runtime errors if an invalid value is passed.

Add validation to ensure the view is valid:

-  const layout = BookerLayouts[view];
+  const layout = BookerLayouts[view] || BookerLayouts.MONTH_VIEW;

Or better yet, validate earlier and use proper typing as suggested in line 43.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const layout = BookerLayouts[view];
const layout = BookerLayouts[view] || BookerLayouts.MONTH_VIEW;
🤖 Prompt for AI Agents
In
packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx
around line 139, the code indexs BookerLayouts with view without validation;
update the component to ensure view is a valid key of BookerLayouts (preferably
by changing the prop/type at line 43 to view: keyof typeof BookerLayouts), and
add a runtime guard before using it: check if view exists in BookerLayouts and
either select a safe default layout or throw/log a clear error if invalid; this
ensures no runtime undefined lookup and aligns runtime checks with the stronger
TypeScript typing.

@supalarry supalarry self-requested a review September 22, 2025 10:43
supalarry
supalarry previously approved these changes Sep 22, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Sep 22, 2025

E2E results are ready!

@Ryukemeister Ryukemeister merged commit 63740c0 into main Sep 22, 2025
39 checks passed
@Ryukemeister Ryukemeister deleted the calendar-view-atom-v1 branch September 22, 2025 12:17
saurabhraghuvanshii pushed a commit to saurabhraghuvanshii/cal.com that referenced this pull request Sep 24, 2025
* fix: refactor

* add `isMonthViewProp` to header

* feat: init v1 for calendar view atom

* test breaking toggle buttons

* fix: make sure week start is always sunday for calendar view atom

* fixup

* fix: remove extra comments

* fix: add calendar view page in examples app

* chore: add changesets

* fix: coderabbit feedback

* fixup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core area: core, team members only ✨ feature New feature or request platform Anything related to our platform plan ready-for-e2e size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants